package de.jmda.mview.proj;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.JTree;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.apache.log4j.Logger;

import de.jmda.mview.DesktopPane;
import de.jmda.mview.DesktopPane.JAXBModel;

public class DiagramsTreeListener
		implements TreeModelListener,	TreeSelectionListener, MouseListener
{
	private final static Logger LOGGER =
			Logger.getLogger(DiagramsTreeListener.class);

	private DiagramsController diagramsController;
	
	/**
	 * shortcut to {@link DiagramsControllerContext#getTreeDiagrams()} in
	 * {@link #diagramsController}
	 */
	private JTree tree;
	
	/**
	 * Reference to most lately selected node in {@link #tree}.
	 */
	private DefaultMutableTreeNode selectedNode;
	
	/**
	 * Stores the tree model user object of the {@link #selectedNode}.
	 * <p>
	 * This is necessary because editing the selected node <b>immediately changes
	 * the tree model user object of that node</b>. Storing the user object as
	 * soon as a node gets selected (see {@link #valueChanged(TreeSelectionEvent)})
	 * makes it possible
	 * <ul>
	 *   <li>to update the underlying user object and
	 *   <li>to restore the selected node's user object
	 * <ul>
	 * after editing has finished (see {@link #treeNodesChanged(TreeModelEvent)}).
	 */
	private Object selectedUserObject;
	
	public DiagramsTreeListener(DiagramsController diagramsController)
	{
		super();
		
		this.diagramsController = diagramsController;

		DiagramsControllerContext controllerContext =
				diagramsController.getDiagramsControllerContext();
		tree = controllerContext.getTreeDiagrams();
		
		if (tree.getSelectionModel().getSelectionMode() ==
		    TreeSelectionModel.SINGLE_TREE_SELECTION)
		{
			throw new IllegalArgumentException(
					"selection mode of tree has to be " +
					"TreeSelectionModel.SINGLE_TREE_SELECTION");
		}
	}

	/**
	 * Updates {@link #selectedNode} and {@link #selectedUserObject} as soon as
	 * the tree selection changes.
	 *
	 * @see javax.swing.event.TreeSelectionListener#valueChanged(javax.swing.event.TreeSelectionEvent)
	 */
	@Override
	public void valueChanged(TreeSelectionEvent treeSelectionEvent)
	{
		Object object = tree.getLastSelectedPathComponent();
		
		if (object == null)
		{
			return;
		}
		
		if (false == object instanceof DefaultMutableTreeNode)
		{
			LOGGER.warn(
					"unexpected type of selected node: " + object.getClass().getName());
			return;
		}
		
		selectedNode = (DefaultMutableTreeNode) object;
		selectedUserObject = selectedNode.getUserObject();
	}

	/**
	 * Determines the changed node in a single tree selection scenario. For that
	 * node
	 * <ul>
	 *   <li>the change to the underlying tree model user object is applied and
	 *   <li>the underlying tree model user object is restored.
	 * </ul>
	 * See {@link #selectedUserObject} for further explanation.
	 *
	 * @see javax.swing.event.TreeModelListener#treeNodesChanged(javax.swing.event.TreeModelEvent)
	 */
	@Override
	public void treeNodesChanged(TreeModelEvent treeModelEvent)
	{
		// determine the changed node
		DefaultMutableTreeNode changedNode;
		
		int[] childIndices = treeModelEvent.getChildIndices();
		
		if (childIndices.length == 0)
		{
			changedNode =
	    		(DefaultMutableTreeNode)
	    				(treeModelEvent.getTreePath().getLastPathComponent());
		}
		else if (childIndices.length == 1)
		{
      int index = treeModelEvent.getChildIndices()[0];
      
      DefaultMutableTreeNode parentOfChangedNode =
	    		(DefaultMutableTreeNode)
	    				(treeModelEvent.getTreePath().getLastPathComponent());
      
      changedNode =
      		(DefaultMutableTreeNode) (parentOfChangedNode.getChildAt(index));
		}
		else
		{
			LOGGER.error(
					"unexpected number of child indices: " + childIndices.length);
			return;
		}
    
    if (selectedUserObject instanceof DesktopPane.JAXBModel)
    {
    	// create typed version of selected user object
    	DesktopPane.JAXBModel selectedUserObjectAsDiagramModel =
    			(DesktopPane.JAXBModel) selectedUserObject;
    	// update typed version of selected user object
    	selectedUserObjectAsDiagramModel.name =
    			changedNode.getUserObject().toString();
    	// restore user object of change node
    	changedNode.setUserObject(selectedUserObjectAsDiagramModel);
    }
    else if (selectedUserObject instanceof DiagramGroup)
    {
    	// create typed version of selected user object
    	DiagramGroup selectedUserObjectAsDiagramGroup =
    			(DiagramGroup) selectedUserObject;
    	// update typed version of selected user object
    	selectedUserObjectAsDiagramGroup.setName(changedNode.getUserObject().toString());
    	// restore user object of change node
    	changedNode.setUserObject(selectedUserObjectAsDiagramGroup);
    }
	}

	@Override
	public void treeNodesInserted(TreeModelEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void treeNodesRemoved(TreeModelEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void treeStructureChanged(TreeModelEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	/**
	 * If possible, determines the {@link DefaultMutableTreeNode} on which the
	 * mouse click was received. If successful delegates to {@link
	 * #mouseClicked(MouseEvent, DefaultMutableTreeNode)}.
	 *
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	@Override
  public void mouseClicked(MouseEvent mouseEvent)
  {
		int selRow = tree.getRowForLocation(mouseEvent.getX(), mouseEvent.getY());

		if(selRow < 0)
		{
			return;
		}
		
		DiagramsTreeNodeClicked diagramsTreeNodeClicked =
				new DiagramsTreeNodeClicked(diagramsController, mouseEvent);
		
		diagramsTreeNodeClicked.handleEvent();
  }

	@Override
  public void mousePressed(MouseEvent e)
  {
	  // TODO Auto-generated method stub
	  
  }

	@Override
  public void mouseReleased(MouseEvent e)
  {
	  // TODO Auto-generated method stub
	  
  }

	@Override
  public void mouseEntered(MouseEvent e)
  {
	  // TODO Auto-generated method stub
	  
  }

	@Override
  public void mouseExited(MouseEvent e)
  {
	  // TODO Auto-generated method stub
	  
  }
}